package com.quectel.mobile.controller.parking;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.func.LambdaUtil;
import com.google.common.base.Preconditions;
import com.quectel.business.common.parking.ParkingBusiness;
import com.quectel.constant.core.parking.CarParkingSpaceConstants;
import com.quectel.constant.core.parking.ParkingChargingConstants;
import com.quectel.constant.core.parking.ParkingOrderConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.constant.pay.PayBusinessType;
import com.quectel.core.module.car.dto.CarDto;
import com.quectel.core.module.car.service.CarService;
import com.quectel.core.module.carparking.dto.CarParkingSpaceDto;
import com.quectel.core.module.carparking.service.CarParkingSpaceService;
import com.quectel.core.module.household.dto.HouseholdDto;
import com.quectel.core.module.mobileuser.dto.MobileUserCarDto;
import com.quectel.core.module.mobileuser.dto.MobileUserDto;
import com.quectel.core.module.mobileuser.service.MobileUserCarService;
import com.quectel.core.module.parking.dto.ParkingChargingDto;
import com.quectel.core.module.parking.dto.ParkingCurrentCarDto;
import com.quectel.core.module.parking.dto.ParkingOrderDto;
import com.quectel.core.module.parking.service.ParkingChargingService;
import com.quectel.core.module.parking.service.ParkingCurrentCarService;
import com.quectel.core.module.parking.service.ParkingOrderService;
import com.quectel.core.module.parkingcard.dto.ParkingCardMemberDto;
import com.quectel.core.module.parkingcard.service.ParkingCardMemberService;
import com.quectel.mobile.controller.BaseController;
import com.quectel.mobile.interceptor.LoginMobileUserSession;
import com.quectel.mobile.util.SessionHolder;
import com.quectel.mobile.vo.parking.CarAddReqVo;
import com.quectel.mobile.vo.parking.PayOrderRespVo;
import com.quectel.util.common.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("parking")
@Tag(name = "ParkingController", description = "车辆停车相关api")
public class ParkingController extends BaseController {

    @DubboReference
    private ParkingOrderService parkingOrderService;
    @DubboReference
    private MobileUserCarService mobileUserCarService;
    @DubboReference
    private CarService carService;
    @DubboReference
    private ParkingCardMemberService parkingCardMemberService;
    @DubboReference
    private CarParkingSpaceService carParkingSpaceService;
    @DubboReference
    private ParkingCurrentCarService parkingCurrentCarService;
    @DubboReference
    private ParkingChargingService parkingChargingService;
    @Autowired
    private ParkingBusiness parkingBusiness;

    /**
     * 停车场收费配置
     * @param villageId
     * @param carType
     * @return
     */
    @GetMapping("getChargingConf")
    @Operation(summary = "停车场收费配置")
    public Response<ParkingChargingDto> getChargingConf(
            @Parameter(description = "小区id", required = true) @RequestParam Long villageId,
            @Parameter(description = "车型 1大车 2小车 默认小车") @RequestParam(required = false) Byte carType
    ) {
        ParkingChargingDto parkingChargingDto = parkingChargingService
                .selectOne(villageId, carType == null ? ParkingChargingConstants.CarTypeEnum.CAR.getCarType() : carType);
        if (Objects.isNull(parkingChargingDto)) {
            return Response.error("该小区收费配置未设置");
        }
        return Response.<ParkingChargingDto>ok().wrap(parkingChargingDto);
    }

    /**
     * 停车账单 根据车辆查询停车账单
     *
     * @param pageNo
     * @param pageSize
     * @return
     */
    @GetMapping("parkingList")
    @Operation(summary = "停车账单")
    public Response<Page<ParkingOrderDto>> parkingList(
            @Parameter(description = "从1开始的页码数", required = true) @RequestParam Integer pageNo,
            @Parameter(description = "每页多少条", required = true) @RequestParam Integer pageSize,
            @Parameter(description = "车牌号") @RequestParam(required = false) String license,
            @Parameter(description = "车牌颜色") @RequestParam(required = false) Byte licenseColor,
            @Parameter(description = "月份筛选 yyyy-MM") @RequestParam(required = false) String monthStr,
            @Parameter(description = "支付状态 0未缴费 1已缴费 2已退款") @RequestParam(required = false) Byte payStatus,
            @Parameter(description = "开票状态 0未提交开票 1申请开票(开票中) 2已开票 3已红冲 4已关闭") @RequestParam(required = false) Byte invoiceStatus
    ) {
        SessionHolder.checkHousehold();

        Request request = Request.configParams(Request.parseParams(httpServletRequest));

        LoginMobileUserSession loginMobileUserSession = SessionHolder.getLoginMobileUserSession();
        Preconditions.checkNotNull(loginMobileUserSession);

        HouseholdDto currentHousehold = loginMobileUserSession.getCurrentHousehold();
        Preconditions.checkNotNull(currentHousehold);

        request.put(LambdaUtil.getFieldName(ParkingOrderDto::getHouseholdId), currentHousehold.getId());
        if (StringUtils.isNotBlank(monthStr)) {
            Date date = DateUtils.parse(monthStr, DateUtils.FormatType.MONTH);
            request.put(SystemConstants.TIME_SLOT_KEY_PAIR.getLeft(), DateUtils.format(DateUtil.beginOfMonth(date), DateUtils.FormatType.COMMON));
            request.put(SystemConstants.TIME_SLOT_KEY_PAIR.getRight(), DateUtils.format(DateUtil.endOfMonth(date), DateUtils.FormatType.COMMON));
        }

        List<ParkingOrderDto> list = null;
        int total = parkingOrderService.queryTotal(request.getParams());
        if (total > 0) {
            list = parkingOrderService.queryList(request.getParams());
        } else {
            list = Lists.newArrayList();
        }
        Page<ParkingOrderDto> page = new Page<>(request.getPageNo(), request.getPageSize(), total, list);
        return Response.<Page<ParkingOrderDto>>ok().wrap(page);
    }

    /**
     * 临停缴费账单 当前这次停车账单，根据在停车辆表拉出订单
     *
     * @param villageId
     * @param license
     * @param licenseColor
     * @return
     */
    @GetMapping("payOrder")
    @Operation(summary = "临停缴费账单")
    public Response<PayOrderRespVo> payOrder(
            @Parameter(description = "小区id", required = true) @RequestParam Long villageId,
            @Parameter(description = "车牌号", required = true) @RequestParam String license,
            @Parameter(description = "车牌颜色", required = true) @RequestParam Byte licenseColor
    ) {
        ParkingCurrentCarDto parkingCurrentCarDto = parkingCurrentCarService.selectOneCar(villageId, license, licenseColor);
        if (Objects.isNull(parkingCurrentCarDto)) {
            return Response.error("车辆未知，请联系车场管理员");
        }
        Long orderId = parkingCurrentCarDto.getOrderId();
        ParkingOrderDto parkingOrderDto = parkingOrderService.selectById(orderId);
        Preconditions.checkNotNull(parkingOrderDto);
        parkingOrderDto.setBusinessType(PayBusinessType.PARKING_FEE);
        if (ParkingOrderConstants.OrderPayStatusEnum.NO_PAY.getPayStatus().equals(parkingOrderDto.getPayStatus())) {
            // 计算出原始订单应付金额
            ParkingChargingDto parkingChargingDto = JacksonUtils.parse(parkingOrderDto.getChargingConfBak(), ParkingChargingDto.class);
            BigDecimal payAmount = parkingBusiness.calAmount(parkingChargingDto, parkingOrderDto.getInTime()
                    , Objects.nonNull(parkingOrderDto.getCrimpingTime()) ? parkingOrderDto.getCrimpingTime() : new Date());
            parkingOrderDto.setDueAmount(payAmount);
        }

        PayOrderRespVo.PayOrderRespVoBuilder resp = PayOrderRespVo.builder().parkingOrder(parkingOrderDto);

        if (ParkingOrderConstants.AdvanceEnum.PAY_ADVANCE.getAdvanceType().equals(parkingOrderDto.getIsAdvance())) {
            // 是提前支付，提前支付才可能存在超时订单
            ParkingOrderDto childOrder = parkingOrderService.selectByPreId(parkingOrderDto.getId());
            if (Objects.nonNull(childOrder)) {
                childOrder.setBusinessType(PayBusinessType.PARKING_FEE);
                resp.isOvertime(Boolean.TRUE)
                        .overTimeOrder(childOrder);
                if (ParkingOrderConstants.OrderPayStatusEnum.NO_PAY.getPayStatus().equals(childOrder.getPayStatus())) {
                    // 计算出当前金额
                    ParkingChargingDto overtimeChargingDto = JacksonUtils.parse(parkingOrderDto.getChargingConfBak(), ParkingChargingDto.class);
                    BigDecimal payAmount = parkingBusiness.calAmount(overtimeChargingDto, childOrder.getInTime()
                            , Objects.nonNull(childOrder.getCrimpingTime()) ? childOrder.getCrimpingTime() : new Date());
                    childOrder.setDueAmount(payAmount);
                }
            }
        }

        // 是否月卡会员
        ParkingCardMemberDto memberDto = parkingCardMemberService.selectValidMember(villageId, license, licenseColor);
        if (Objects.nonNull(memberDto)) {
            resp.isCardMember(Boolean.TRUE);
        }

        return Response.<PayOrderRespVo>ok().wrap(resp.build());
    }

    /**
     * 临停历史缴费账单，通过openId查询停车缴费账单
     *
     * @param pageNo
     * @param pageSize
     * @return
     */
    @GetMapping("orderList")
    @Operation(summary = "临停历史缴费账单")
    public Response<Page<ParkingOrderDto>> orderList(
            @Parameter(description = "从1开始的页码数", required = true) @RequestParam Integer pageNo,
            @Parameter(description = "每页多少条", required = true) @RequestParam Integer pageSize
    ) {
        Request request = Request.configParams(Request.parseParams(httpServletRequest));

        LoginMobileUserSession loginMobileUserSession = SessionHolder.getLoginMobileUserSession();
        Preconditions.checkNotNull(loginMobileUserSession);

        String buyerId = SystemConstants.buildPlatformUserUniqueFlag(loginMobileUserSession.getOfficialAccountsType()
                , loginMobileUserSession.getAppId(), loginMobileUserSession.getOpenId());
        request.put(LambdaUtil.getFieldName(ParkingOrderDto::getBuyerId), buyerId);
//        request.put(LambdaUtil.getFieldName(ParkingOrderDto::getPayStatus), ParkingOrderConstants.OrderPayStatusEnum.PAY_SUCCESS.getPayStatus());
        List<ParkingOrderDto> list = null;
        int total = parkingOrderService.queryTotal(request.getParams());
        if (total > 0) {
            list = parkingOrderService.queryList(request.getParams());
        } else {
            list = Lists.newArrayList();
        }
        Page<ParkingOrderDto> page = new Page<>(request.getPageNo(), request.getPageSize(), total, list);
        return Response.<Page<ParkingOrderDto>>ok().wrap(page);
    }


    /**
     * 添加mobileUser车辆
     * @param carAddReqVo
     * @return
     */
    @PostMapping("addCar")
    @Operation(summary = "添加用户车辆")
    public Response<Long> addCar(@RequestBody @Validated CarAddReqVo carAddReqVo) {
        SessionHolder.checkMobileUser();
        MobileUserDto mobileUser = Objects.requireNonNull(SessionHolder.getLoginMobileUserSession()).getMobileUser();

        MobileUserCarDto oldDto = mobileUserCarService
                .selectOne(mobileUser.getId(), carAddReqVo.getLicense(), carAddReqVo.getLicenseColor());
        if (Objects.nonNull(oldDto)) {
            return Response.error("车辆已存在，请前往完善信息");
        }
        MobileUserCarDto mobileUserCarDto = CopyUtils.copyObj(carAddReqVo, MobileUserCarDto.class);
        mobileUserCarDto.setMobileUserId(mobileUser.getId());
        mobileUserCarDto.setCreateTime(new Date());
        Long save = mobileUserCarService.save(mobileUserCarDto);
        return Response.<Long>ok().wrap(save);
    }

    /**
     * 完善用户车辆
     * @param mobileUserCar
     * @return
     */
    @PostMapping("updateCar")
    @Operation(summary = "完善用户车辆")
    public Response<Object> updateCar(@RequestBody @Validated MobileUserCarDto mobileUserCar) {
        SessionHolder.checkMobileUser();
        MobileUserDto mobileUser = Objects.requireNonNull(SessionHolder.getLoginMobileUserSession()).getMobileUser();

        // 完善车辆信息
        MobileUserCarDto oldDto = mobileUserCarService.selectCacheById(mobileUserCar.getId());
        if (Objects.isNull(oldDto) || !mobileUser.getId().equals(oldDto.getMobileUserId())) {
            return Response.error("车辆不存在，请先添加");
        }
        mobileUserCar.setEditTime(new Date());
        mobileUserCarService.updateById(mobileUserCar);
        return Response.ok();
    }

    /**
     * 删除用户车辆，只能是未认证车辆
     * @param id
     * @return
     */
    @PostMapping("delCar")
    @Operation(summary = "删除用户车辆")
    public Response<Object> addCar(@RequestBody Long id) {
        SessionHolder.checkMobileUser();
        MobileUserDto mobileUser = Objects.requireNonNull(SessionHolder.getLoginMobileUserSession()).getMobileUser();

        MobileUserCarDto oldDto = mobileUserCarService.selectCacheById(id);
        if (Objects.nonNull(oldDto) && mobileUser.getId().equals(oldDto.getMobileUserId())) {
            mobileUserCarService.deleteById(oldDto.getId());
        }
        return Response.ok();
    }

    /**
     * 我的车辆 可能查出其他小区的已认证车辆(待确定)
     * 包含注册车辆与mobileUser车辆，需要去重
     * @return
     */
    @GetMapping("cars")
    @Operation(summary = "我的车辆")
    public Response<List<MobileUserCarDto>> cars(){
        SessionHolder.checkHousehold();
        HouseholdDto currentHousehold = Objects.requireNonNull(SessionHolder.getLoginMobileUserSession()).getCurrentHousehold();
        List<CarDto> carDtos = carService.selectByMobile(currentHousehold.getMobile());
        Map<String, CarDto> carMap = new HashMap<>();
        for (CarDto carDto : carDtos) {
            CarDto oldCar = carMap.putIfAbsent(carDto.getLicense() + "_" + carDto.getLicenseColor(), carDto);
            if (Objects.nonNull(oldCar) && currentHousehold.getVillageId().equals(carDto.getVillageId())) {
                carMap.put(carDto.getLicense() + "_" + carDto.getLicenseColor(), carDto);
            }
        }

        List<MobileUserCarDto> allCars = CopyUtils.copyList(new ArrayList<>(carMap.values()), MobileUserCarDto.class);
        for (MobileUserCarDto mobileUserCarDto : allCars) {
            // 全部为已认证车辆，可能是别的小区的，不过也认为属于 '我的' 车辆
            mobileUserCarDto.setIsCertified(Boolean.TRUE);

            mobileUserCarDto.setParkingCardMember(parkingCardMemberService.selectOneMember(currentHousehold.getVillageId()
                    , mobileUserCarDto.getLicense(), mobileUserCarDto.getLicenseColor()));
        }

        List<MobileUserCarDto> mobileUserCarDtoList = mobileUserCarService.selectByMobile(currentHousehold.getMobile());
        for (MobileUserCarDto mobileUserCarDto : mobileUserCarDtoList) {
            if (!carMap.containsKey(mobileUserCarDto.getLicense() + "_" + mobileUserCarDto.getLicenseColor())) {
                mobileUserCarDto.setIsCertified(Boolean.FALSE);
                allCars.add(mobileUserCarDto);
            }
        }
        return Response.<List<MobileUserCarDto>>ok().wrap(allCars);
    }

    /**
     * 我的车位包含当前小区购买的和办卡租赁到的
     * @return
     */
    @GetMapping("parkingPlaces")
    @Operation(summary = "我的车位")
    public Response<List<CarParkingSpaceDto>> parkingPlaces(){
        SessionHolder.checkHousehold();
        HouseholdDto currentHousehold = Objects.requireNonNull(SessionHolder.getLoginMobileUserSession()).getCurrentHousehold();

        Map<String, Object> querySpaceParams = new HashMap<>();
        querySpaceParams.put(LambdaUtil.getFieldName(CarParkingSpaceDto::getVillageId), currentHousehold.getVillageId());
        querySpaceParams.put(LambdaUtil.getFieldName(CarParkingSpaceDto::getMobile), currentHousehold.getMobile());
        querySpaceParams.put(LambdaUtil.getFieldName(CarParkingSpaceDto::getState), CarParkingSpaceConstants.SpaceStateEnum.SOLD.getState());
        List<CarParkingSpaceDto> result = carParkingSpaceService.queryList(querySpaceParams);

        // 通过月卡办理租赁到的车位
        List<CarParkingSpaceDto> leasedList = carParkingSpaceService.byCardLeased(currentHousehold.getMobile());

        result.addAll(leasedList.parallelStream()
                .filter(o -> currentHousehold.getVillageId().equals(o.getVillageId()))
                .collect(Collectors.toList()));
        return Response.<List<CarParkingSpaceDto>>ok().wrap(result);
    }
}
